package com.homer.gps.track.persist;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import android.util.Log;

import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.PreparedQuery;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.stmt.SelectArg;

public class Repository {
	private final static String LOG_TAG = "Repository";
	
	private Dao<RouteModel, Integer> routeDao;
	private Dao<PointModel, Integer> pointDao;
	private Dao<PlaceModel, Integer> placeDao;
	private Dao<StopModel, Integer> stopDao;
	private Dao<StopsHasPlacesModel, Integer> stophasplacesDao;

	public Repository(final DatabaseHelper databaseHelper) {
		this.routeDao = getRouteDao(databaseHelper);
		this.pointDao = getPointDao(databaseHelper);
		this.placeDao = getPlaceDao(databaseHelper);
		this.stopDao = getStopDao(databaseHelper);
		this.stophasplacesDao = getStopsHasPlacesDao(databaseHelper);
	}
	
//	public void clearData() {
//		final List<PointModel> points = getPoints();
//		for (final PointModel point : points) {
//			deletePoint(point);
//		}
//	}
	
	public List<RouteModel> getRoutes() {
		try {
			QueryBuilder<RouteModel, Integer> routeQb = routeDao.queryBuilder();
			routeQb.orderBy(RouteModel.ID_FIELD_NAME, false);
			return this.routeDao.query(routeQb.prepare());
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
		return new ArrayList<RouteModel>();
	}
	
	public List<PointModel> getPoints() {
		try {
			return this.pointDao.queryForAll();
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
		return new ArrayList<PointModel>();
	}
	
	public List<PlaceModel> getPlaces() {
		try {
			return this.placeDao.queryForAll();
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
		return new ArrayList<PlaceModel>();
	}
	
	private Dao<RouteModel, Integer> getRouteDao(final DatabaseHelper databaseHelper) {
		if (null == this.routeDao) {
			try {
				this.routeDao = databaseHelper.getRouteDao();
			}
			catch (final SQLException e) {
				Log.e(LOG_TAG, "Unable to load DAO: " + e.getMessage());
				e.printStackTrace();
			}
		}
		return this.routeDao;
	}
	
	private Dao<PointModel, Integer> getPointDao(final DatabaseHelper databaseHelper) {
		if (null == this.pointDao) {
			try {
				this.pointDao = databaseHelper.getPointDao();
			}
			catch (final SQLException e) {
				Log.e(LOG_TAG, "Unable to load DAO: " + e.getMessage());
				e.printStackTrace();
			}
		}
		return this.pointDao;
	}
	
	private Dao<PlaceModel, Integer> getPlaceDao(final DatabaseHelper databaseHelper) {
		if (null == this.placeDao) {
			try {
				this.placeDao = databaseHelper.getPlaceDao();
			}
			catch (final SQLException e) {
				Log.e(LOG_TAG, "Unable to load DAO: " + e.getMessage());
				e.printStackTrace();
			}
		}
		return this.placeDao;
	}
	
	private Dao<StopModel, Integer> getStopDao(final DatabaseHelper databaseHelper) {
		if (null == this.stopDao) {
			try {
				this.stopDao = databaseHelper.getStopDao();
			}
			catch (final SQLException e) {
				Log.e(LOG_TAG, "Unable to load DAO: " + e.getMessage());
				e.printStackTrace();
			}
		}
		return this.stopDao;
	}
	
	private Dao<StopsHasPlacesModel, Integer> getStopsHasPlacesDao(final DatabaseHelper databaseHelper) {
		if (null == this.stophasplacesDao) {
			try {
				this.stophasplacesDao = databaseHelper.getStopsHasPlacesDao();
			}
			catch (final SQLException e) {
				Log.e(LOG_TAG, "Unable to load DAO: " + e.getMessage());
				e.printStackTrace();
			}
		}
		return this.stophasplacesDao;
	}
	
	public void deleteRoute(final RouteModel route) {
		try {
//			final ForeignCollection<PointModel> points = route.getPoints();
//			for (final PointModel point : points) {
//				this.pointDao.delete(point);
//			}
			this.routeDao.delete(route);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
	}
	
	public void deletePoint(final PointModel point) {
		try {
			this.pointDao.delete(point);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
	}
	
	public void deletePlace(final PlaceModel place) {
		try {
			this.placeDao.delete(place);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
	}
	
	public void deleteStop(final StopModel stop) {
		try {
			this.stopDao.delete(stop);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
	}
	
	public void saveOrUpdateRoute(final RouteModel route) {
		try {
			this.routeDao.createOrUpdate(route);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
	}
	
	public void saveOrUpdatePoint(final PointModel point) {
		try {
			this.pointDao.createOrUpdate(point);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
	}
	
	public void saveOrUpdatePlace(final PlaceModel place) {
		try {
			this.placeDao.createOrUpdate(place);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
	}
	
	public void saveOrUpdateStop(final StopModel stop) {
		try {
			this.stopDao.createOrUpdate(stop);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
	}
	
	public void saveOrUpdateStopsHasPlaces(final StopsHasPlacesModel stopsHasPlaces) {
		try {
			this.stophasplacesDao.createOrUpdate(stopsHasPlaces);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
	}
	
	public RouteModel getRouteForId(final Integer routeId) {
		RouteModel route = new RouteModel();
		route = null;
		try {
			route = this.routeDao.queryForId(routeId);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
		return route;
	}
	
	public PlaceModel getPlaceForId(final Integer placeId) {
		PlaceModel place = new PlaceModel();
		place = null;
		try {
			place = this.placeDao.queryForId(placeId);
		}
		catch (final SQLException e) {
			e.printStackTrace();
		}
		return place;
	}
	
	public List<PointModel> lookupPointsForRoute(final RouteModel route) throws SQLException{
		QueryBuilder<PointModel, Integer> pointQb = pointDao.queryBuilder();
		pointQb.where().eq(PointModel.ID_FIELD_ROUTE_ID, route.getId());
		return pointDao.query(pointQb.prepare());
	}
	
	public List<StopModel> lookupStopsForRoute(final RouteModel route, int stopType) throws SQLException{
		QueryBuilder<StopModel, Integer> stopQb = stopDao.queryBuilder();
		stopQb.where().eq(StopModel.ID_FIELD_TYPE, stopType).and().eq(StopModel.ID_FIELD_ROUTE_ID, route.getId());
		return stopDao.query(stopQb.prepare());
	}
	
//	public long getCount() throws SQLException{
//		return pointDao.countOf();
//	}
	
	/*
	 * Convenience methods to build and run our prepared queries.
	 */

	private PreparedQuery<PlaceModel> placesForStopQuery = null;

	public PlaceModel lookupPlacesForStop(StopModel stop) throws SQLException {
		if (placesForStopQuery == null) {
			placesForStopQuery = makePlacesForStopQuery();
		}
		placesForStopQuery.setArgumentHolderValue(0, stop);
		return placeDao.queryForFirst(placesForStopQuery);
	}

	/**
	 * Build our query for Post objects that match a User.
	 */
	private PreparedQuery<PlaceModel> makePlacesForStopQuery() throws SQLException {
		// build our inner query for StopsHasPlaces objects
		QueryBuilder<StopsHasPlacesModel, Integer> stopsHasPlacesQb = stophasplacesDao.queryBuilder();
		// just select the place-id field
		stopsHasPlacesQb.selectColumns(StopsHasPlacesModel.PLACE_ID_FIELD_NAME);
		SelectArg stopSelectArg = new SelectArg();
		// you could also just pass in stop1 here
		stopsHasPlacesQb.where().eq(StopsHasPlacesModel.STOP_ID_FIELD_NAME, stopSelectArg);

		// build our outer query for Place objects
		QueryBuilder<PlaceModel, Integer> placeQb = placeDao.queryBuilder();
		// where the id matches in the place-id from the inner query
		placeQb.where().in(PlaceModel.ID_FIELD_NAME, stopsHasPlacesQb);
		return placeQb.prepare();
	}

}
